﻿using Microsoft.Extensions.Caching.Memory;

namespace System;

/// <summary>
/// 系统缓存
/// </summary>
public abstract class MemoryCacheHelper
{
    private static readonly MemoryCache Mc = new(new MemoryCacheOptions());

    #region ======== 通用操作 ========

    /// <summary>
    /// 缓存对象是否存在
    /// </summary>
    private static bool Exists(string key)
    {
        if (key == null) throw new ArgumentNullException(nameof(key));
        return Mc.TryGetValue(key, out _);
    }

    /// <summary>
    /// 获取所有缓存键
    /// </summary>
    public static List<string> AllKeys()
    {
        const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
        var entries = Mc.GetType().GetField("_entries", flags)?.GetValue(Mc);
        var keys = new List<string>();
        if (entries is not IDictionary cacheItems) return keys;
        keys.AddRange(from DictionaryEntry cacheItem in cacheItems select cacheItem.Key.ToString());
        return keys;
    }

    #endregion

    #region ======== 添加缓存 ========

    /// <summary>
    /// 添加缓存
    /// </summary>
    public static bool Set(string key, object value)
    {
        if (key == null) throw new ArgumentNullException(nameof(key));
        if (value == null) throw new ArgumentNullException(nameof(value));
        Mc.Set(key, value);
        return Exists(key);
    }

    /// <summary>
    /// 添加缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    /// <param name="value">缓存Value</param>
    /// <param name="expiresIn">缓存时长</param>
    /// <param name="isSliding">是否滑动过期(有效期内有操作,则以当前时间点延长过期时间)</param>
    /// <returns></returns>
    public static bool Set(string key, object value, TimeSpan expiresIn, bool isSliding = false)
    {
        if (key == null) throw new ArgumentNullException(nameof(key));
        if (value == null) throw new ArgumentNullException(nameof(value));
        Mc.Set(key, value, isSliding
            ? new MemoryCacheEntryOptions().SetSlidingExpiration(expiresIn)
            : new MemoryCacheEntryOptions().SetAbsoluteExpiration(expiresIn));
        return Exists(key);
    }

    /// <summary>
    /// 添加缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    /// <param name="value">缓存Value</param>
    /// <param name="expiresSliding">是否滑动过期(有效期内有操作,则以当前时间点延长过期时间)</param>
    /// <param name="expiresAbsolute">绝对过期时长</param>
    /// <returns></returns>
    public static bool Set(string key, object value, TimeSpan expiresSliding, TimeSpan expiresAbsolute)
    {
        if (key == null) throw new ArgumentNullException(nameof(key));
        if (value == null) throw new ArgumentNullException(nameof(value));
        Mc.Set(key, value, new MemoryCacheEntryOptions()
            .SetSlidingExpiration(expiresSliding)
            .SetAbsoluteExpiration(expiresAbsolute));
        return Exists(key);
    }

    #endregion

    #region ======== 读取缓存 ========

    /// <summary>
    /// 获取缓存
    /// </summary>
    public static T Get<T>(string key) where T : class
    {
        if (key == null) throw new ArgumentNullException(nameof(key));
        return Mc.Get(key) as T;
    }

    /// <summary>
    /// 获取缓存
    /// </summary>
    public static object Get(string key)
    {
        if (key == null) throw new ArgumentNullException(nameof(key));
        return Mc.Get(key);
    }

    /// <summary>
    /// 获取缓存集合
    /// </summary>
    public static IDictionary<string, object> GetAll(IEnumerable<string> keys)
    {
        if (keys == null) throw new ArgumentNullException(nameof(keys));
        var dict = new Dictionary<string, object>();
        keys.ToList().ForEach(item => dict.Add(item, Mc.Get(item)));
        return dict;
    }

    #endregion

    #region ======== 移除缓存 ========

    /// <summary>
    /// 删除缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    /// <returns></returns>
    public static void Remove(string key)
    {
        if (key == null) throw new ArgumentNullException(nameof(key));
        Mc.Remove(key);
    }

    /// <summary>
    /// 批量删除缓存
    /// </summary>
    public static void Remove(IEnumerable<string> keys)
    {
        if (keys == null) throw new ArgumentNullException(nameof(keys));
        keys.ToList().ForEach(item => Mc.Remove(item));
    }

    /// <summary>
    /// 删除所有缓存
    /// </summary>
    public static void RemoveAll()
    {
        Remove(AllKeys());
    }


    /// <summary>
    /// 删除匹配到的缓存
    /// </summary>
    /// <param name="pattern">正则表达式</param>
    public static void RemovePattern(string pattern)
    {
        var matchedKeys = AllKeys().Where(x => Regex.IsMatch(x, pattern)).ToList().AsReadOnly();
        foreach (var key in matchedKeys)
        {
            Mc.Remove(key);
        }
    }

    #endregion
}